import ISpriteContainer from "../interfaces/ISpriteContainer";
import ISprite from "../interfaces/ISprite";
import IDispatcher from "../interfaces/IDispatcher";
import EOrder from "../enums/EOrder";
import CanvasKeyBoardEvent from "../events/CanvasKeyBoardEvent";
import CanvasMouseEvent from "../events/CanvasMouseEvent";
import EInputEventType from "../enums/EInputEventType";
import Matrix2D from "../maths/Matrix2D";
import Math2D from "../maths/Math2D";

export default class Sprite2DManager implements ISpriteContainer, IDispatcher {

    public sprite: ISprite | undefined = undefined;
    public name: string = 'sprite2dManager';
    private _sprites: ISprite[] = [];   // 数组存放ISprite接口的实例对象

    // 由_dragSprite接受drag事件的处理
    // 也就是说,drag事件都是发送到_dragSprite精灵的
    private _dragSprite: ISprite | undefined = undefined;

    public addSprite(sprite: ISprite): ISpriteContainer {
        sprite.owner = this;
        this._sprites.push(sprite);
        return this;
    }

    public removeSpriteAt(idx: number): void {
        // 数组的splice方法：从idx开始，删除1个元素
        this._sprites.splice(idx, 1);
    }

    public removeSprite(sprite: ISprite): boolean {
        // 根据sprite查找索引号
        let idx: number = this.getSpriteIndex(sprite);
        if (idx !== -1) {
            // 如果找到，就删除，并返回true
            this.removeSpriteAt(idx);
            return true;
        }
        // 如果没找到，返回false
        return false;
    }

    public removeAll(): void {
        // 我们有4种数组情况的方法，具体来看一下
        /* // 第1种方式，调用我们自己实现的removeSpriteAt方法
        for(let i:number = this._sprites.length - 1; i>=0;i--){
            this.removeSpriteAt(i);
        } */

        /* // 第2种方式，调用数组的pop方法
        for(let i:number=this._sprites.length-1;i>=0;i--){
            this._sprites.pop();
        } */

        /* // 第3种方式，直接将数组的length设置为0
        this._sprites.length = 0; */

        // 目前我们使用第4种方式，重新生成一个新数组赋值给this._sprite变量
        this._sprites = [];
    }


    public getSprite(idx: number): ISprite {
        if (idx < 0 || idx > this._sprites.length - 1) {
            throw new Error('参数idx越界!!!');
        }
        return this._sprites[idx];
    }

    public getSpriteCount(): number {
        return this._sprites.length;
    }

    public getSpriteIndex(sprite: ISprite): number {
        for (let i: number = 0; i < this._sprites.length; i++) {
            if (this._sprites[i] === sprite) {
                return i;
            }
        }
        return -1;
    }

    public getParentSprite(): ISprite | undefined {
        return undefined;
    }

    public get container(): ISpriteContainer {
        return this;
    }

    // 分发update事件
    public dispatchUpdate(msec: number, diff: number): void {
        // 从前到后遍历精灵数组,触发PREORDER updateEvent
        for (let i: number = 0; i < this._sprites.length; i++) {
            this._sprites[i].update(msec, diff, EOrder.PREORDER);
        }
        // 从后到前遍历精灵数组,触发POSTORDER updateEvent
        for (let i: number = this._sprites.length - 1; i >= 0; i--) {
            this._sprites[i].update(msec, diff, EOrder.POSTORDER);
        }
    }

    // 分发draw命令
    public dispatchDraw(context: CanvasRenderingContext2D): void {
        // 从前到后遍历精灵数组,调用draw方法
        // 绘制的顺序是先添加的精灵先绘制,后添加的精灵后绘制
        for (let i: number = 0; i < this._sprites.length; i++) {
            this._sprites[i].draw(context);
        }
    }

    // 分发键盘事件,采取最简单的方式
    // 遍历每个精灵,凡是有键盘处理事件的都触发该事件
    public dispatchKeyEvent(evt: CanvasKeyBoardEvent): void {
        let spr: ISprite;
        for (let i: number = 0; i < this._sprites.length; i++) {
            spr = this._sprites[i];
            if (spr.keyEvent) {
                spr.keyEvent(spr, evt);
            }
        }
    }

    // 分发鼠标事件
    public dispatchMouseEvent(evt: CanvasMouseEvent): void {
        // 每次按下鼠标时,将_dragSprite设置为当前鼠标指针下面的那个精灵
        // 每次释放鼠标时,将_dragSprite设置为undefined
        if (evt.type === EInputEventType.MOUSEUP) {
            this._dragSprite = undefined;
        } else if (evt.type === EInputEventType.MOUSEDRAG) {
            // 触发drag事件,由_dragSprite接受并处理
            if (this._dragSprite !== undefined) {
                if (this._dragSprite.mouseEvent !== null) {
                    this._dragSprite.mouseEvent(this._dragSprite, evt);
                    // 一旦处理完成,不再继续分发,直接退出方法
                    return;
                }
            }
        }
        let spr: ISprite;
        // 遍历精灵数组，注意是从后向前的顺序遍历
        // 绘制时，以由前往后方式遍历，这样可以保证绘制的深度正确性
        // 绘制鼠标事件时，以由后往前方式遍历，确保上面的精灵能先接收到事件
        for (let i: number = this._sprites.length - 1; i >= 0; i--) {
            // 获取当前精灵
            spr = this._sprites[i];
            // 获取当前精灵的局部矩阵
            let mat: Matrix2D | null = spr.getLocalMatrix();
            // 将全局表示的canvasPosition点变换到相对当前精灵局部坐标系的表示: localPosition
            Matrix2D.transform(mat, evt.canvasPosition, evt.localPosition);
            // 要测试的点和精灵必须在同一个坐标系中，切记
            // 如果碰撞检测试成功，说明选中该精灵
            if (spr.hitTest(evt.localPosition)) {
                evt.hasLocalPosition = true;
                // 鼠标按下并且有点选选中的精灵时，记录下来
                // 后续的drag事件都是发送到该精灵上的
                if (evt.type === EInputEventType.MOUSEDOWN) {
                    this._dragSprite = spr;
                }
                // 如果有选中的精灵，并且有事件处理程序，则立刻触发该事件处理程序
                if (spr.mouseEvent) {
                    spr.mouseEvent(spr, evt);
                    // 一旦处理完成，不再继续分发，直接退出方法
                    return;
                }
            }
        }
    }
}